﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Kokomo.Mirror.CliMetadata.Signatures
{
	public class MethodDefSignature : Signature
	{
		internal MethodDefSignature()
		{
		}

		public SignatureFlags flags;
		public int genericParameterCount;
		public ReturnType returnType;
		public Param[] parameters;
		public int? varArgStartIndex;

		public bool HasReturnValue()
		{
			if (this.returnType == null) return false;
			PrimitiveTypeSpec primitive = this.returnType.type as PrimitiveTypeSpec;
			if ((primitive == null) || (primitive.elementType != SignatureElementType.Void)) return true;
			return false;
		}
		public bool Matches(
			MethodBase method,
			Module module,
			Type[] genericTypeArguments,
			Type[] genericMethodArguments
			)
		{
			if ((object)method == null) throw new ArgumentNullException("method");

			ParameterInfo[] parameters = method.GetParameters();
			if (this.parameters.Length != parameters.Length)
				return false;

			for (int i = 0; i < parameters.Length; i++)
			{
				Param param = this.parameters[i];
				ParameterInfo parameter = parameters[i];

				bool paramMatches = param.Matches(parameter, module, genericTypeArguments, genericMethodArguments);
				if (!paramMatches) return false;
			}

			MethodInfo methodInfo = method as MethodInfo;
			if ((object)methodInfo != null)
			{
				ParameterInfo returnParameter = methodInfo.ReturnParameter;
				if (this.HasReturnValue())
				{
					if (
						(returnParameter == null)
						//|| !this.returnType.Matches(returnParameter, module)
						)
						return false;
				}
				else
				{
					if (
						(returnParameter != null)
						&& !MemberEqualityComparer.Equal(returnParameter.ParameterType, typeof(void))
						)
						return false;
				}
			}

			return true;
		}
		public bool Matches(
			Module ownerModule,
			MethodDefSignature other,
			Module otherModule
			)
		{
			if ((object)ownerModule == null) throw new ArgumentNullException("ownerModule");
			if (other == null) throw new ArgumentNullException("other");
			if ((object)otherModule == null) throw new ArgumentNullException("otherModule");

			Param[] parameters = other.parameters;
			if (this.parameters.Length != parameters.Length)
				return false;

			if (
				(this.returnType != null)
				&& (other.returnType != null)
				)
			{
				if (!this.returnType.Matches(ownerModule, other.returnType, otherModule))
					return false;
			}
			else if (
				   (this.returnType == null)
				   || (other.returnType == null)
				   )
				return false;

			for (int i = 0; i < parameters.Length; i++)
			{
				Param param = this.parameters[i];
				Param otherParam = parameters[i];

				bool paramMatches = param.Matches(ownerModule, otherParam, otherModule);
				if (!paramMatches) return false;
			}

			return true;
		}
	}
}
